
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
// 
//   http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

/**
 * AUTO-GENERATED FILE. DO NOT MODIFY.
 */

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
// 
//   http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
import { __extends } from 'tslib';
/**
 * Separate legend and scrollable legend to reduce package size.
 */

import * as zrUtil from 'zrender/lib/core/util.js';
import * as graphic from '../../util/graphic.js';
import * as layoutUtil from '../../util/layout.js';
import LegendView from './LegendView.js';
var Group = graphic.Group;
var WH = ['width', 'height'];
var XY = ['x', 'y'];

var ScrollableLegendView =
/** @class */
function (_super) {
	__extends(ScrollableLegendView, _super);

	function ScrollableLegendView() {
		var _this = _super !== null && _super.apply(this, arguments) || this;

		_this.type = ScrollableLegendView.type;
		_this.newlineDisabled = true;
		_this._currentIndex = 0;
		return _this;
	}

	ScrollableLegendView.prototype.init = function () {
		_super.prototype.init.call(this);

		this.group.add(this._containerGroup = new Group());

		this._containerGroup.add(this.getContentGroup());

		this.group.add(this._controllerGroup = new Group());
	};
	/**
   * @override
   */

	ScrollableLegendView.prototype.resetInner = function () {
		_super.prototype.resetInner.call(this);

		this._controllerGroup.removeAll();

		this._containerGroup.removeClipPath();

		this._containerGroup.__rectSize = null;
	};
	/**
   * @override
   */

	ScrollableLegendView.prototype.renderInner = function (itemAlign, legendModel, ecModel, api, selector, orient, selectorPosition) {
		var self = this; // Render content items.

		_super.prototype.renderInner.call(this, itemAlign, legendModel, ecModel, api, selector, orient, selectorPosition);

		var controllerGroup = this._controllerGroup; // FIXME: support be 'auto' adapt to size number text length,
		// e.g., '3/12345' should not overlap with the control arrow button.

		var pageIconSize = legendModel.get('pageIconSize', true);
		var pageIconSizeArr = zrUtil.isArray(pageIconSize) ? pageIconSize : [pageIconSize, pageIconSize];
		createPageButton('pagePrev', 0);
		var pageTextStyleModel = legendModel.getModel('pageTextStyle');
		controllerGroup.add(new graphic.Text({
			name: 'pageText',
			style: {
				// Placeholder to calculate a proper layout.
				text: 'xx/xx',
				fill: pageTextStyleModel.getTextColor(),
				font: pageTextStyleModel.getFont(),
				verticalAlign: 'middle',
				align: 'center'
			},
			silent: true
		}));
		createPageButton('pageNext', 1);

		function createPageButton(name, iconIdx) {
			var pageDataIndexName = name + 'DataIndex';
			var icon = graphic.createIcon(legendModel.get('pageIcons', true)[legendModel.getOrient().name][iconIdx], {
				// Buttons will be created in each render, so we do not need
				// to worry about avoiding using legendModel kept in scope.
				onclick: zrUtil.bind(self._pageGo, self, pageDataIndexName, legendModel, api)
			}, {
				x: -pageIconSizeArr[0] / 2,
				y: -pageIconSizeArr[1] / 2,
				width: pageIconSizeArr[0],
				height: pageIconSizeArr[1]
			});
			icon.name = name;
			controllerGroup.add(icon);
		}
	};
	/**
   * @override
   */

	ScrollableLegendView.prototype.layoutInner = function (legendModel, itemAlign, maxSize, isFirstRender, selector, selectorPosition) {
		var selectorGroup = this.getSelectorGroup();
		var orientIdx = legendModel.getOrient().index;
		var wh = WH[orientIdx];
		var xy = XY[orientIdx];
		var hw = WH[1 - orientIdx];
		var yx = XY[1 - orientIdx];
		selector && layoutUtil.box( // Buttons in selectorGroup always layout horizontally
			'horizontal', selectorGroup, legendModel.get('selectorItemGap', true));
		var selectorButtonGap = legendModel.get('selectorButtonGap', true);
		var selectorRect = selectorGroup.getBoundingRect();
		var selectorPos = [-selectorRect.x, -selectorRect.y];
		var processMaxSize = zrUtil.clone(maxSize);
		selector && (processMaxSize[wh] = maxSize[wh] - selectorRect[wh] - selectorButtonGap);

		var mainRect = this._layoutContentAndController(legendModel, isFirstRender, processMaxSize, orientIdx, wh, hw, yx, xy);

		if (selector) {
			if (selectorPosition === 'end') {
				selectorPos[orientIdx] += mainRect[wh] + selectorButtonGap;
			} else {
				var offset = selectorRect[wh] + selectorButtonGap;
				selectorPos[orientIdx] -= offset;
				mainRect[xy] -= offset;
			}

			mainRect[wh] += selectorRect[wh] + selectorButtonGap;
			selectorPos[1 - orientIdx] += mainRect[yx] + mainRect[hw] / 2 - selectorRect[hw] / 2;
			mainRect[hw] = Math.max(mainRect[hw], selectorRect[hw]);
			mainRect[yx] = Math.min(mainRect[yx], selectorRect[yx] + selectorPos[1 - orientIdx]);
			selectorGroup.x = selectorPos[0];
			selectorGroup.y = selectorPos[1];
			selectorGroup.markRedraw();
		}

		return mainRect;
	};

	ScrollableLegendView.prototype._layoutContentAndController = function (legendModel, isFirstRender, maxSize, orientIdx, wh, hw, yx, xy) {
		var contentGroup = this.getContentGroup();
		var containerGroup = this._containerGroup;
		var controllerGroup = this._controllerGroup; // Place items in contentGroup.

		layoutUtil.box(legendModel.get('orient'), contentGroup, legendModel.get('itemGap'), !orientIdx ? null : maxSize.width, orientIdx ? null : maxSize.height);
		layoutUtil.box( // Buttons in controller are layout always horizontally.
			'horizontal', controllerGroup, legendModel.get('pageButtonItemGap', true));
		var contentRect = contentGroup.getBoundingRect();
		var controllerRect = controllerGroup.getBoundingRect();
		var showController = this._showController = contentRect[wh] > maxSize[wh]; // In case that the inner elements of contentGroup layout do not based on [0, 0]

		var contentPos = [-contentRect.x, -contentRect.y]; // Remain contentPos when scroll animation perfroming.
		// If first rendering, `contentGroup.position` is [0, 0], which
		// does not make sense and may cause unexepcted animation if adopted.

		if (!isFirstRender) {
			contentPos[orientIdx] = contentGroup[xy];
		} // Layout container group based on 0.

		var containerPos = [0, 0];
		var controllerPos = [-controllerRect.x, -controllerRect.y];
		var pageButtonGap = zrUtil.retrieve2(legendModel.get('pageButtonGap', true), legendModel.get('itemGap', true)); // Place containerGroup and controllerGroup and contentGroup.

		if (showController) {
			var pageButtonPosition = legendModel.get('pageButtonPosition', true); // controller is on the right / bottom.

			if (pageButtonPosition === 'end') {
				controllerPos[orientIdx] += maxSize[wh] - controllerRect[wh];
			} // controller is on the left / top.
			else {
				containerPos[orientIdx] += controllerRect[wh] + pageButtonGap;
			}
		} // Always align controller to content as 'middle'.

		controllerPos[1 - orientIdx] += contentRect[hw] / 2 - controllerRect[hw] / 2;
		contentGroup.setPosition(contentPos);
		containerGroup.setPosition(containerPos);
		controllerGroup.setPosition(controllerPos); // Calculate `mainRect` and set `clipPath`.
		// mainRect should not be calculated by `this.group.getBoundingRect()`
		// for sake of the overflow.

		var mainRect = {
			x: 0,
			y: 0
		}; // Consider content may be overflow (should be clipped).

		mainRect[wh] = showController ? maxSize[wh] : contentRect[wh];
		mainRect[hw] = Math.max(contentRect[hw], controllerRect[hw]); // `containerRect[yx] + containerPos[1 - orientIdx]` is 0.

		mainRect[yx] = Math.min(0, controllerRect[yx] + controllerPos[1 - orientIdx]);
		containerGroup.__rectSize = maxSize[wh];

		if (showController) {
			var clipShape = {
				x: 0,
				y: 0
			};
			clipShape[wh] = Math.max(maxSize[wh] - controllerRect[wh] - pageButtonGap, 0);
			clipShape[hw] = mainRect[hw];
			containerGroup.setClipPath(new graphic.Rect({
				shape: clipShape
			})); // Consider content may be larger than container, container rect
			// can not be obtained from `containerGroup.getBoundingRect()`.

			containerGroup.__rectSize = clipShape[wh];
		} else {
			// Do not remove or ignore controller. Keep them set as placeholders.
			controllerGroup.eachChild(function (child) {
				child.attr({
					invisible: true,
					silent: true
				});
			});
		} // Content translate animation.

		var pageInfo = this._getPageInfo(legendModel);

		pageInfo.pageIndex != null && graphic.updateProps(contentGroup, {
			x: pageInfo.contentPosition[0],
			y: pageInfo.contentPosition[1]
		}, // When switch from "show controller" to "not show controller", view should be
		// updated immediately without animation, otherwise causes weird effect.
		showController ? legendModel : null);

		this._updatePageInfoView(legendModel, pageInfo);

		return mainRect;
	};

	ScrollableLegendView.prototype._pageGo = function (to, legendModel, api) {
		var scrollDataIndex = this._getPageInfo(legendModel)[to];

		scrollDataIndex != null && api.dispatchAction({
			type: 'legendScroll',
			scrollDataIndex: scrollDataIndex,
			legendId: legendModel.id
		});
	};

	ScrollableLegendView.prototype._updatePageInfoView = function (legendModel, pageInfo) {
		var controllerGroup = this._controllerGroup;
		zrUtil.each(['pagePrev', 'pageNext'], function (name) {
			var key = name + 'DataIndex';
			var canJump = pageInfo[key] != null;
			var icon = controllerGroup.childOfName(name);

			if (icon) {
				icon.setStyle('fill', canJump ? legendModel.get('pageIconColor', true) : legendModel.get('pageIconInactiveColor', true));
				icon.cursor = canJump ? 'pointer' : 'default';
			}
		});
		var pageText = controllerGroup.childOfName('pageText');
		var pageFormatter = legendModel.get('pageFormatter');
		var pageIndex = pageInfo.pageIndex;
		var current = pageIndex != null ? pageIndex + 1 : 0;
		var total = pageInfo.pageCount;
		pageText && pageFormatter && pageText.setStyle('text', zrUtil.isString(pageFormatter) ? pageFormatter.replace('{current}', current == null ? '' : current + '').replace('{total}', total == null ? '' : total + '') : pageFormatter({
			current: current,
			total: total
		}));
	};
	/**
   *  contentPosition: Array.<number>, null when data item not found.
   *  pageIndex: number, null when data item not found.
   *  pageCount: number, always be a number, can be 0.
   *  pagePrevDataIndex: number, null when no previous page.
   *  pageNextDataIndex: number, null when no next page.
   * }
   */

	ScrollableLegendView.prototype._getPageInfo = function (legendModel) {
		var scrollDataIndex = legendModel.get('scrollDataIndex', true);
		var contentGroup = this.getContentGroup();
		var containerRectSize = this._containerGroup.__rectSize;
		var orientIdx = legendModel.getOrient().index;
		var wh = WH[orientIdx];
		var xy = XY[orientIdx];

		var targetItemIndex = this._findTargetItemIndex(scrollDataIndex);

		var children = contentGroup.children();
		var targetItem = children[targetItemIndex];
		var itemCount = children.length;
		var pCount = !itemCount ? 0 : 1;
		var result = {
			contentPosition: [contentGroup.x, contentGroup.y],
			pageCount: pCount,
			pageIndex: pCount - 1,
			pagePrevDataIndex: null,
			pageNextDataIndex: null
		};

		if (!targetItem) {
			return result;
		}

		var targetItemInfo = getItemInfo(targetItem);
		result.contentPosition[orientIdx] = -targetItemInfo.s; // Strategy:
		// (1) Always align based on the left/top most item.
		// (2) It is user-friendly that the last item shown in the
		// current window is shown at the begining of next window.
		// Otherwise if half of the last item is cut by the window,
		// it will have no chance to display entirely.
		// (3) Consider that item size probably be different, we
		// have calculate pageIndex by size rather than item index,
		// and we can not get page index directly by division.
		// (4) The window is to narrow to contain more than
		// one item, we should make sure that the page can be fliped.

		for (var i = targetItemIndex + 1, winStartItemInfo = targetItemInfo, winEndItemInfo = targetItemInfo, currItemInfo = null; i <= itemCount; ++i) {
			currItemInfo = getItemInfo(children[i]);

			if ( // Half of the last item is out of the window.
				!currItemInfo && winEndItemInfo.e > winStartItemInfo.s + containerRectSize || // If the current item does not intersect with the window, the new page
      // can be started at the current item or the last item.
      currItemInfo && !intersect(currItemInfo, winStartItemInfo.s)) {
				if (winEndItemInfo.i > winStartItemInfo.i) {
					winStartItemInfo = winEndItemInfo;
				} else {
					// e.g., when page size is smaller than item size.
					winStartItemInfo = currItemInfo;
				}

				if (winStartItemInfo) {
					if (result.pageNextDataIndex == null) {
						result.pageNextDataIndex = winStartItemInfo.i;
					}

					++result.pageCount;
				}
			}

			winEndItemInfo = currItemInfo;
		}

		for (var i = targetItemIndex - 1, winStartItemInfo = targetItemInfo, winEndItemInfo = targetItemInfo, currItemInfo = null; i >= -1; --i) {
			currItemInfo = getItemInfo(children[i]);

			if ( // If the the end item does not intersect with the window started
			// from the current item, a page can be settled.
				(!currItemInfo || !intersect(winEndItemInfo, currItemInfo.s)) && // e.g., when page size is smaller than item size.
      winStartItemInfo.i < winEndItemInfo.i) {
				winEndItemInfo = winStartItemInfo;

				if (result.pagePrevDataIndex == null) {
					result.pagePrevDataIndex = winStartItemInfo.i;
				}

				++result.pageCount;
				++result.pageIndex;
			}

			winStartItemInfo = currItemInfo;
		}

		return result;

		function getItemInfo(el) {
			if (el) {
				var itemRect = el.getBoundingRect();
				var start = itemRect[xy] + el[xy];
				return {
					s: start,
					e: start + itemRect[wh],
					i: el.__legendDataIndex
				};
			}
		}

		function intersect(itemInfo, winStart) {
			return itemInfo.e >= winStart && itemInfo.s <= winStart + containerRectSize;
		}
	};

	ScrollableLegendView.prototype._findTargetItemIndex = function (targetDataIndex) {
		if (!this._showController) {
			return 0;
		}

		var index;
		var contentGroup = this.getContentGroup();
		var defaultIndex;
		contentGroup.eachChild(function (child, idx) {
			var legendDataIdx = child.__legendDataIndex; // FIXME
			// If the given targetDataIndex (from model) is illegal,
			// we use defaultIndex. But the index on the legend model and
			// action payload is still illegal. That case will not be
			// changed until some scenario requires.

			if (defaultIndex == null && legendDataIdx != null) {
				defaultIndex = idx;
			}

			if (legendDataIdx === targetDataIndex) {
				index = idx;
			}
		});
		return index != null ? index : defaultIndex;
	};

	ScrollableLegendView.type = 'legend.scroll';
	return ScrollableLegendView;
}(LegendView);

export default ScrollableLegendView;